package com.ewancle;

import com.alibaba.fastjson2.JSON;
import com.ewancle.model.KnowlCat;
import com.ewancle.utils.PostgreSqlJdbc;
import com.ewancle.utils.SnowflakeIdGenerator;
import org.apache.commons.io.FileUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.interactions.PointerInput;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.sql.SQLException;
import java.time.Duration;
import java.util.List;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

public class SeleniumTestKnowlCat {

    public static void main(String[] args) {

        String os = System.getProperty("os.name");
        //Windows操作系统
        if (os != null && os.toLowerCase().startsWith("windows")) {
            System.out.println(String.format("当前系统版本是:%s", os));
            System.setProperty("webdriver.chrome.driver", "src/test/resources/chromedriver.exe");
        } else if (os != null && os.toLowerCase().startsWith("linux")) {//Linux操作系统
            System.out.println(String.format("当前系统版本是:%s", os));
            System.setProperty("webdriver.chrome.driver", "src/test/resources/chromedriver");
        } else { //其它操作系统
            System.out.println(String.format("当前系统版本是:%s", os));
        }

        ChromeOptions options = new ChromeOptions();
        // 模拟安卓设备，设置设备为 Nexus 5X
        Map<String, Object> mobileEmulation = new HashMap<>();
        //mobileEmulation.put("deviceName", "Nexus 9X");
        mobileEmulation.put("width", 1440);  // Nexus 9X 屏幕宽度（假设）
        mobileEmulation.put("height", 2560); // Nexus 9X 屏幕高度（假设）
        mobileEmulation.put("pixelRatio", 3.0);  // Nexus 9X 像素比（假设）
        options.setExperimentalOption("mobileEmulation", mobileEmulation);
        // 设置设备屏幕尺寸和视口，模拟手机端
        options.addArguments("--window-size=1440,2560");  // iPhone 6分辨率

        // 设置其它 Chrome 配置选项
        options.addArguments("--disable-blink-features=AutomationControlled"); // 禁用自动化特性,在某些网站上，Selenium 可能被检测为自动化工具，可以通过禁用 webdriver 特性来绕过检测。
        options.addArguments("--disable-extensions");  // 禁用扩展

        options.addArguments("--disable-gpu");  // 禁用 GPU

        options.addArguments("Accept-Language", "en-US,en;q=0.9");
        //options.addArguments("Accept-Encoding", "gzip, deflate, br");
        options.addArguments("Connection", "keep-alive");
        options.addArguments("Upgrade-Insecure-Requests", "1");
        options.setExperimentalOption("useAutomationExtension", false); // 通过 setExperimentalOption 启用一些实验性的 Chrome 功能

        // 设置 User-Agent 和其他请求头，模拟真实的安卓设备浏览器
        options.addArguments("user-agent=Mozilla/7.0 (Linux; Android 13.1.0; Nexus 9X Build/OPM4.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.4472.77 Mobile Safari/537.36");
        WebDriver driver = null;
        try {
            // 创建 WebDriver 实例
            driver = new ChromeDriver(options);

            // 修改 navigator.webdriver 属性，避免自动化脚本的检测。
            //修改 WebDriver 属性: 某些网站会通过 navigator.webdriver 判断是否是自动化浏览器。可以通过 JavaScript 设置这些属性来避免被识别为 Selenium
            JavascriptExecutor js = (JavascriptExecutor) driver;
            js.executeScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})");

            // 访问网页
            driver.get("https://www.ahuyikao.com/login/passwordlogin.html");
            // 等待页面加载（这里使用 JavaScript 来确保页面完全加载）
            waitForPageLoad(driver);
            // 避免过快的请求频率，模拟正常用户的浏览行为。设置合理的等待时间和点击间隔。
            // 随机等待 3 到 7 秒，模拟正常用户行为
            Thread.sleep(3000 + (long) (Math.random() * 4000));

            // 选择器的使用https://www.selenium.dev/zh-cn/documentation/webdriver/elements/locators/
            // 定位输入框，并输入文本（使用 By 类来选择网页元素，如 By.id(), By.name(), By.xpath() 等。）
            WebElement inputField = driver.findElement(By.id("phones"));
            inputField.sendKeys("13337839087");

            WebElement inputField1 = driver.findElement(By.id("passwords"));
            inputField1.sendKeys("yunsheng6088");

            // 定位按钮并点击
            WebElement button = driver.findElement(By.xpath("//*[@id=\"login_password_form\"]/div[3]/button"));
            button.click();
            Thread.sleep(10000);

            // 保存验证码(把缺口和无缺口图标截屏保存）
            /*saveCode(driver);
            try {
                // 缺口起始点x坐标
                int left = getGap();
                // 验证码的边界差值
                left -= 7;
                // 计算人操作的滑动轨迹
                List<Integer> moveTrace = trace(left);
                move(moveTrace, driver);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }*/

            // 切换到弹窗并接受它
            //Alert alert = driver.switchTo().alert();
            //alert.accept();  // 点击“确定”

            // 切换到弹窗并获取文本
            //Alert alert = driver.switchTo().alert();
            //String alertText = alert.getText(); //获取弹窗的文本信息
            //System.out.println("弹窗文本: " + alertText);
            // 关闭弹窗
            //alert.accept();  // 或者 alert.dismiss();

            // 切换到弹窗
            //Alert alert = driver.switchTo().alert();
            // 向弹窗输入文本 向 prompt 弹窗中输入文本
            //alert.sendKeys("Hello, world!");
            // 接受弹窗
            //alert.accept();  // 或者 alert.dismiss();

            // 切换到弹窗并拒绝它
            //Alert alert = driver.switchTo().alert();
            //alert.dismiss();  // 点击“取消”

            // 显式等待直到弹窗出现
            //WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); // 最多等待10秒
            //wait.until(ExpectedConditions.alertIsPresent());
            // 切换到弹窗并接受它
            //Alert alert = driver.switchTo().alert();
            //alert.accept();  // 或者 alert.dismiss();
            //alert.dismiss();

            // 查找并点击 Layui 弹窗的关闭按钮
            WebElement closeButton = driver.findElement(By.xpath("//*[@id=\"layui-layer2\"]/div[3]/a"));
            closeButton.click();  // 点击关闭按钮

            // 定位到拖动条或图片滑块元素
            /*WebElement slider = driver.findElement(By.id("slider"));
            // 使用 Actions 类模拟拖动操作
            Actions actions = new Actions(driver);
            actions.clickAndHold(slider)  // 按住拖动条
                    .moveByOffset(200, 0)  // 向右移动200px
                    .release()  // 松开鼠标
                    .perform();  // 执行动作
            // 可选：检查验证码是否通过（例如检查元素是否改变）
            WebElement result = driver.findElement(By.id("result"));
            if (result.getText().equals("验证通过")) {
                System.out.println("验证码通过！");
            }*/

            // 模拟鼠标移动和点击
            Actions actions = new Actions(driver);
            WebElement element = driver.findElement(By.xpath("/html/body/div[2]/div[1]/div/div[1]/div/ul/li[4]/a"));
            actions.moveToElement(element).perform();

            //WebElement element1 = driver.findElement(By.xpath("/html/body/div[2]/div[1]/div/div[1]/div/ul/li[4]/ul/li[1]"));
            //element1.click();
            // 使用显式等待
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
            // 等待子菜单项出现
            WebElement submenu = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("/html/body/div[2]/div[1]/div/div[1]/div/ul/li[4]/ul/li[1]")));
            // 点击子菜单项
            submenu.click();

            // 执行 JavaScript 代码来关闭 Layui 弹窗
            //JavascriptExecutor js = (JavascriptExecutor) driver;
            // 假设弹窗的索引是 1，使用 layer.close(index) 来关闭弹窗
            //js.executeScript("layer.close(1);");

            // 使用 JavaScript 获取所有弹窗索引，并关闭最后一个弹窗
            //JavascriptExecutor js = (JavascriptExecutor) driver;
            //js.executeScript("var index = layer.index; layer.close(index);");

            // 显式等待直到弹窗出现
            //WebDriverWait wait = new WebDriverWait(driver, 10);
            //wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".layui-layer")));
            // 查找并点击 Layui 弹窗的关闭按钮
            //WebElement closeButton = driver.findElement(By.cssSelector(".layui-layer-close"));
            //closeButton.click();  // 点击关闭按钮

            //WebElement buttonVip = driver.findElement(By.xpath("/html/body/div[2]/div[4]/div[1]/nav/div[2]/div/div/ul/li[4]/ul/li[1]/a"));
            //buttonVip.click();  // 点击VIP题库按钮

            /*Set<String> windowHandles = driver.getWindowHandles();
            for (String handle : windowHandles) {
                System.out.println("多个窗口："+handle);
                driver.switchTo().window(handle);
                // 执行操作
            }*/

            //Thread.sleep(30000);

            // 3. 获取当前窗口的句柄
            String originalWindow = driver.getWindowHandle();
            // 4. 等待新窗口打开
            WebDriverWait waitNew = new WebDriverWait(driver, Duration.ofSeconds(20));
            waitNew.until(ExpectedConditions.numberOfWindowsToBe(2));  // 等待窗口数为2

            // 5. 获取所有窗口句柄
            Set<String> windowHandles = driver.getWindowHandles();

            // 6. 切换到新打开的窗口
            for (String handle : windowHandles) {
                if (!handle.equals(originalWindow)) {
                    driver.switchTo().window(handle);  // 切换到新窗口
                    break;
                }
            }

            // 10. 关闭新窗口并切换回原窗口
            /*driver.close();  // 关闭当前窗口（即新窗口）
            driver.switchTo().window(originalWindow);  // 切换回原窗口*/

            Thread.sleep(10000);

            // 模拟鼠标移动和点击
            Actions actions1 = new Actions(driver);
            WebElement element1 = driver.findElement(By.xpath("/html/body/div[3]/div/a/img"));
            actions1.moveToElement(element1).click().perform();


            // 通过 Selenium 操作网页
            String pageSource = driver.getPageSource();  // 获取网页源代码
            //System.out.println(pageSource);

            // 获取所有的 cookie 信息
            Set<Cookie> cookies = driver.manage().getCookies();
            Map<String, String> cookieMap = new HashMap<>();
            // 将 HTML 转换为 Jsoup 的 Document 对象
            Document doc = Jsoup.parse(pageSource);
            // 使用 Jsoup 进行元素查找和操作
            System.out.println(doc.title());  // 输出网页标题
            // 查找所有的 img 标签 （通过浏览器查找选择器的 copy -> selector )
            // Elements images = doc.select("#pills-tab > li:nth-child(1) > a");
            Elements liInA = doc.select("#pills-tab > li > a");
            List<KnowlCat> knowlCatParentList = new ArrayList<>();
            //System.out.println(images.html());
            Set<Long> ids = new HashSet<>();
            for (Element lia : liInA) {

                SnowflakeIdGenerator generator = new SnowflakeIdGenerator(ThreadLocalRandom.current().nextInt(32), ThreadLocalRandom.current().nextInt(32));
                //SnowflakeIdGenerator generator = new SnowflakeIdGenerator((new Random()).nextInt(32), (new Random()).nextInt(32));
                long id = generator.generateId();
                while (ids.contains(id)){
                    id = generator.generateId();
                }
                ids.add(id);
                KnowlCat knowlCat = new KnowlCat();
                String ariaControls = lia.attr("aria-controls");  // 获取图片的 src 属性值
                String href = lia.attr("href");  // 获取图片的 href 属性值
                String text = lia.text();        // 获取链接文本
                knowlCat.setId(id);
                knowlCat.setParentId(0L);
                knowlCat.setName(text);
                knowlCat.setHref(href);
                knowlCatParentList.add(knowlCat);
                System.out.println("href: " + href);
                /*System.out.println("aria-controls: " + ariaControls);
                System.out.println("href: " + href);
                System.out.println("li下a标签的内容: " + text);*/
                //System.out.println(image.html());
                // 获取自分类
                // tr:nth-child(even) p 会选择偶数行 <tr> 内所有嵌套的 <p> 元素，包括嵌套在子元素中的 <p>。
                // tr:nth-child(even) > p 只选择 <tr> 下直接包含 <p> 元素的情况。
                Elements liInASubList = doc.select(href+" > div > div:nth-child(odd) p:nth-child(1)"); // 选择奇数行的 <tr> 第一行开始算
                /*String desc = liInASubList.text();
                List<String> descList = liInASubList.eachText();
                int index = 0;
                Elements liInASubHead = doc.select(href+" > div > div:nth-child(even) > div p"); //第二行开始
                //Elements liInASubHead = doc.select(href+" > div > div:nth-child(even) > p"); // 选择偶数行的 <tr>
                //System.out.println("liInASubHead = "+liInASubHead.html());
                //System.out.println("liInASubList = "+liInASubList.html());
                for (Element subClass : liInASubHead) {
                    KnowlCat knowlCatSub = new KnowlCat();
                    Integer mainCid = Integer.valueOf(subClass.attr("data"));
                    SnowflakeIdGenerator generatorSub = new SnowflakeIdGenerator(ThreadLocalRandom.current().nextInt(32), ThreadLocalRandom.current().nextInt(32));
                    long subId = generatorSub.generateId();
                    while (ids.contains(subId)){
                        subId = generatorSub.generateId();
                    }
                    ids.add(subId);
                    knowlCatSub.setId(subId);
                    knowlCatSub.setDesc(desc);
                    knowlCatSub.setMainCid(mainCid);
                    knowlCatSub.setName(subClass.text());
                    knowlCatSub.setParentId(id);
                    knowlCatParentList.add(knowlCatSub);
                }*/
                int indexStart = 1;
                for(Element subHead :liInASubList){
                    String desc = subHead.text();
                    Elements liInASubHead = doc.select(href+" > div > div:nth-child("+(indexStart*2)+") > div p");
                    for (Element subClass : liInASubHead) {
                        KnowlCat knowlCatSub = new KnowlCat();
                        Integer mainCid = Integer.valueOf(subClass.attr("data"));
                        SnowflakeIdGenerator generatorSub = new SnowflakeIdGenerator(ThreadLocalRandom.current().nextInt(32), ThreadLocalRandom.current().nextInt(32));
                        long subId = generatorSub.generateId();
                        while (ids.contains(subId)){
                            subId = generatorSub.generateId();
                        }
                        ids.add(subId);
                        knowlCatSub.setId(subId);
                        knowlCatSub.setDesc(desc);
                        knowlCatSub.setMainCid(mainCid);
                        knowlCatSub.setName(subClass.text());
                        knowlCatSub.setParentId(id);
                        knowlCatParentList.add(knowlCatSub);
                    }
                    indexStart++;
                }
            }

            //System.out.println("全部的列表信息="+JSON.toJSONString(knowlCatParentList));
            System.out.println("全部的列表信息总共="+knowlCatParentList.size());
            String insertknowlCatSql = "INSERT INTO t_knowl_cat (id,name,parent_id,href,main_cid,\"desc\") "
                    + "VALUES (?, ?, ?, ?, ?, ? );";
            int addCount = 0;
            try {
                addCount = PostgreSqlJdbc.batchInsertKnowlCat(knowlCatParentList, insertknowlCatSql, 100 );
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
            System.out.println("添加"+addCount+"条");
            // 查找输入框元素
            /*Element inputElement = doc.select("input[name=search]").first();
            if (inputElement != null) {
                String value = inputElement.val();  // 获取 input 的 value 值
                System.out.println("Input Value: " + value);
            }*/
            // 查找文本框（textarea）元素
            /*Element textareaElement = doc.select("textarea[name=comment]").first();
            if (textareaElement != null) {
                String text = textareaElement.val();  // 获取 textarea 的内容
                System.out.println("Textarea Value: " + text);
            }*/
            //

            // 打印每个 cookie 的信息
            for (Cookie cookie : cookies) {
                /*System.out.println("Cookie Name: " + cookie.getName());
                System.out.println("Cookie Value: " + cookie.getValue());
                System.out.println("Cookie Domain: " + cookie.getDomain());
                System.out.println("Cookie Path: " + cookie.getPath());
                System.out.println("Cookie Expiry: " + cookie.getExpiry());
                System.out.println("Cookie Secure: " + cookie.isSecure());
                System.out.println("Cookie HttpOnly: " + cookie.isHttpOnly());
                System.out.println("----------------------------------------");*/
                cookieMap.put(cookie.getName(), cookie.getValue());
            }
            System.out.println("cookieMap : "+JSON.toJSONString(cookieMap));

            String android = "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ2A.190305.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36";
            String iphone = "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2 like Mac OS X) AppleWebKit/537.36 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/537.36";
            String wechat = "Mozilla/5.0 (Linux; Android 10; Mi 9T Pro Build/QKQ1.190910.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.120 Mobile Safari/537.36 MicroMessenger/8.0.17.2010(0x2800113B) NetType/WIFI Language/zh_CN";
            String pc = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";

            // 目标 URL
            String referer = "https://waph.ahuyk.com";
            String url = "https://www.example.com";
            // 构造自定义请求头
            Map<String, String> headers = new HashMap<>();
            headers.put("User-Agent", "Mozilla/5.0 (Linux; Android 10; Mi 9T Pro Build/QKQ1.190910.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.120 Mobile Safari/537.36 MicroMessenger/8.0.17.2010(0x2800113B) NetType/WIFI Language/zh_CN");
            headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
            headers.put("Accept-Encoding", "gzip, deflate, br");
            headers.put("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
            headers.put("Connection", "keep-alive");
            headers.put("Referer", referer); // 如果需要Referer信息
            headers.put("Upgrade-Insecure-Requests", "1");
            headers.put("Cache-Control", "max-age=0");

            // 使用 Jsoup 发送请求，并获取 HTML 页面
            /*Document document = null;
            try {
                document = Jsoup.connect(url)
                        .headers(headers)  // 设置请求头
                        .cookies(cookieMap)
                        .timeout(10000)     // 设置超时时间
                        .get();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            // 打印抓取到的 HTML 页面
            System.out.println(document.title());
            System.out.println(document.body());*/

            // mainCid 是选择大分类下面小分类的data属性值（中级职称: 中西医结合内科学 106和护理学 = 110）
            // https://waph.ahuyk.com/ahuxueshu/user/SetMainCategory.html post Form Data mainCid = 106跳转到某科目的ip课题列表

            // 在课题列表页面，信息 = 有做题进度信息，onclick=window.location.href='/ahutiku/book/index/tikuType/1/bookId/157.html' 的进行跳转
            // 跳转页面有科目列表 每个列表有 <a href="/ahutiku/QuePart/GetPartList.html?partId=6226"

            // 选择子科目
            //https://waph.ahuyk.com/ahutiku/PrefQue/getQueList.html?dir_type=0&part_id=6227&returnUrl=http%3A%2F%2Fwaph.ahuyk.com%2Fahutiku%2FQuePart%2FGetPartList.html%3FpartId%3D6226


        } catch (InterruptedException e) {
            driver.close();
            throw new RuntimeException(e);
        } finally {
            /*if (driver != null) {
                driver.close();
            }*/
            // 获取所有打开的窗口句柄
            Set<String> windowHandles = driver.getWindowHandles();
            Iterator<String> iterator = windowHandles.iterator();
            // 获取原窗口的句柄
            String originalWindow = iterator.next();
            // 获取新窗口的句柄
            String newWindow = iterator.next();
            // 切换到新窗口
            driver.switchTo().window(newWindow);
            // 在新窗口中执行操作
            System.out.println("Current window title: " + driver.getTitle());
            // 关闭新窗口
            driver.close();
            // 切换回原窗口
            driver.switchTo().window(originalWindow);
            // 在原窗口中执行其他操作
            System.out.println("Back to original window: " + driver.getTitle());
            // 关闭浏览器
            driver.quit();
        }
    }

    public static void tf(String[] args) {

        System.setProperty("webdriver.chrome.driver", "src/test/resources/chromedriver");

        // 设置 ChromeOptions
        ChromeOptions options = new ChromeOptions();

        // 模拟安卓设备，设置设备为 Nexus 5X
        Map<String, Object> mobileEmulation = new HashMap<>();
        //mobileEmulation.put("deviceName", "Nexus 9X");
        mobileEmulation.put("width", 1440);  // Nexus 9X 屏幕宽度（假设）
        mobileEmulation.put("height", 2560); // Nexus 9X 屏幕高度（假设）
        mobileEmulation.put("pixelRatio", 3.0);  // Nexus 9X 像素比（假设）
        options.setExperimentalOption("mobileEmulation", mobileEmulation);
        // 设置设备屏幕尺寸和视口，模拟手机端
        options.addArguments("--window-size=1440,2560");  // iPhone 6分辨率

        // 设置其它 Chrome 配置选项
        options.addArguments("--disable-blink-features=AutomationControlled"); // 禁用自动化特性,在某些网站上，Selenium 可能被检测为自动化工具，可以通过禁用 webdriver 特性来绕过检测。
        options.addArguments("--disable-extensions");  // 禁用扩展

        options.addArguments("--disable-gpu");  // 禁用 GPU

        options.addArguments("Accept-Language", "en-US,en;q=0.9");
        options.addArguments("Accept-Encoding", "gzip, deflate, br");
        options.addArguments("Connection", "keep-alive");
        options.addArguments("Upgrade-Insecure-Requests", "1");
        options.setExperimentalOption("useAutomationExtension", false); // 通过 setExperimentalOption 启用一些实验性的 Chrome 功能

        // 设置 User-Agent 和其他请求头，模拟真实的安卓设备浏览器
        options.addArguments("user-agent=Mozilla/7.0 (Linux; Android 13.1.0; Nexus 9X Build/OPM4.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.4472.77 Mobile Safari/537.36");

        // 创建 WebDriver 实例
        WebDriver driver = new ChromeDriver(options);

        // 修改 navigator.webdriver 属性，避免自动化脚本的检测。
        //修改 WebDriver 属性: 某些网站会通过 navigator.webdriver 判断是否是自动化浏览器。可以通过 JavaScript 设置这些属性来避免被识别为 Selenium
        JavascriptExecutor js = (JavascriptExecutor) driver;
        js.executeScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})");

        try {
            // 访问网页
            driver.get("https://www.ahuyikao.com/login/passwordlogin.html");
            // 等待页面加载（这里使用 JavaScript 来确保页面完全加载）
            waitForPageLoad(driver);
            // 避免过快的请求频率，模拟正常用户的浏览行为。设置合理的等待时间和点击间隔。
            // 随机等待 3 到 7 秒，模拟正常用户行为
            Thread.sleep(3000 + (long) (Math.random() * 4000));

            // 选择器的使用https://www.selenium.dev/zh-cn/documentation/webdriver/elements/locators/
            // 定位输入框，并输入文本（使用 By 类来选择网页元素，如 By.id(), By.name(), By.xpath() 等。）
            WebElement inputField = driver.findElement(By.id("phones"));
            inputField.sendKeys("13337839087");

            WebElement inputField1 = driver.findElement(By.id("passwords"));
            inputField1.sendKeys("yunsheng6088");

            // 定位按钮并点击
            WebElement button = driver.findElement(By.xpath("//*[@id=\"login_password_form\"]/div[3]/button"));
            button.click();

            // 模拟回车键
            //Actions actions = new Actions(driver);
            //actions.sendKeys(inputField, "\\u000D").perform();  // "\\u000D" 是回车键的 Unicode 编码

            // 创建一个 Actions 对象来模拟右键点击
            // 定位到某个元素（例如右键点击的链接）
            //WebElement link = driver.findElement(By.linkText("Example Link"));
            //Actions actions = new Actions(driver);
            //actions.contextClick(link).perform();  // 右键点击

            // 定位拖拽源和目标元素
            //WebElement sourceElement = driver.findElement(By.id("drag-source"));
            //WebElement targetElement = driver.findElement(By.id("drop-target"));
            // 创建 Actions 对象来模拟拖拽操作
            //Actions actions = new Actions(driver);
            //actions.dragAndDrop(sourceElement, targetElement).perform();

            // 随机等待 3 到 7 秒，模拟正常用户行为
            Thread.sleep(3000 + (long) (Math.random() * 4000));
            // 获取所有的 cookie 信息
            Set<Cookie> cookies = driver.manage().getCookies();

            // 打印每个 cookie 的信息
            for (Cookie cookie : cookies) {
                System.out.println("Cookie Name: " + cookie.getName());
                System.out.println("Cookie Value: " + cookie.getValue());
                System.out.println("Cookie Domain: " + cookie.getDomain());
                System.out.println("Cookie Path: " + cookie.getPath());
                System.out.println("Cookie Expiry: " + cookie.getExpiry());
                System.out.println("Cookie Secure: " + cookie.isSecure());
                System.out.println("Cookie HttpOnly: " + cookie.isHttpOnly());
                System.out.println("----------------------------------------");
            }
            // 获取指定名称的 cookie
            Cookie authCookie = driver.manage().getCookieNamed("auth_token");
            if (authCookie != null) {
                System.out.println("Auth Token Cookie Value: " + authCookie.getValue());
            }

            // 保存 cookies 到文件
            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cookies.ser"))) {
                oos.writeObject(cookies);
            } catch (Exception e) {
                e.printStackTrace();
            }

            // 加载之前保存的 cookies
            //loadCookies(driver);
            // 刷新页面以应用 cookies
            //driver.navigate().refresh();

            // 创建 JavascriptExecutor 实例
            JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
            // 执行 JavaScript 代码
            jsExecutor.executeScript("alert('Hello, Selenium!');");

            // 创建 JavascriptExecutor 实例
            JavascriptExecutor jsExecutor1 = (JavascriptExecutor) driver;
            // 执行 JavaScript，获取页面的标题
            String pageTitle = (String) jsExecutor1.executeScript("return document.title;");

            // 执行 JavaScript 代码，修改页面中的元素文本
            //jsExecutor.executeScript("document.getElementById('elementId').innerText = 'New Text';");
            // 使用 JavaScript 模拟点击事件
            //jsExecutor.executeScript("arguments[0].click();", button);
            // 执行异步 JavaScript 代码
            jsExecutor.executeAsyncScript(
                    "var callback = arguments[arguments.length - 1];" +
                            "setTimeout(function(){ callback('Async script executed'); }, 3000);"
            );

            // 执行截屏
            TakesScreenshot screenshot = (TakesScreenshot) driver;
            File srcFile = screenshot.getScreenshotAs(OutputType.FILE);

            // 保存截图到本地
            File destFile = new File("screenshot.png");
            //FileUtils.copyFile(srcFile, destFile);

            // 输出保存截图的路径
            System.out.println("Screenshot saved to: " + destFile.getAbsolutePath());


            // 获取页面中的某个元素
            WebElement element = driver.findElement(By.id("someElementId"));
            // 创建 JavascriptExecutor 实例
            //JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
            // 使用 JavaScript 修改元素的样式，添加边框来框选
            jsExecutor.executeScript("arguments[0].style.border='3px solid red';", element);
            // 更复杂
            /*jsExecutor.executeScript(
                    "arguments[0].style.border='5px solid yellow'; arguments[0].style.transition='border 0.5s ease';",
                    element
            );*/
            // 等待 2 秒钟查看效果（这里使用 Thread.sleep，实际中最好使用显式等待）
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 恢复元素的原始样式（如果需要）
            jsExecutor.executeScript("arguments[0].style.border='';", element);


            // 获取页面中的某个元素
            //WebElement element = driver.findElement(By.id("someElementId"));
            // 创建 Actions 实例
            Actions actions = new Actions(driver);
            // 模拟鼠标悬停
            actions.moveToElement(element).perform();
            // 模拟点击
            actions.click(element).perform();

            // 输出页面标题
            System.out.println("Page Title: " + pageTitle);

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            driver.close();
        }

    }

    /*方法 2：通过 AutoHotkey 与 Selenium 结合使用（更灵活）
    如果你需要更多的自动化控制，可以使用 AutoHotkey 脚本与 Selenium 配合，来更精细地控制 FastStone Capture 的滚动截屏。AutoHotkey 可以模拟键盘和鼠标操作，可以通过快捷键控制 FastStone Capture 来进行截图。

    步骤： https://www.autohotkey.com/
    编写 AutoHotkey 脚本，通过快捷键触发 FastStone Capture 截图。
    通过 Selenium 控制浏览器，触发滚动操作。
    通过 AutoHotkey 脚本控制 FastStone Capture，进行滚动截屏。
    AutoHotkey 的优势在于它能模拟更复杂的桌面交互，因此适合需要频繁控制 FastStone Capture 的场景。*/
    // 编写faststone_capture.ahk控制脚本
    public static void autoHotkey(String[] args) throws InterruptedException, IOException {
        // 设置 ChromeDriver 路径
        System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");

        // 启动浏览器
        WebDriver driver = new ChromeDriver();

        // 打开网页
        driver.get("https://www.example.com");

        // 滚动页面，确保内容加载完毕
        JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
        jsExecutor.executeScript("window.scrollTo(0, document.body.scrollHeight);");
        Thread.sleep(2000);  // 等待页面加载

        // 调用 AutoHotkey 脚本来执行滚动截屏
        String ahkScriptPath = "C:\\path\\to\\faststone_capture.ahk";
        String command = "C:\\Program Files\\AutoHotkey\\AutoHotkey.exe " + ahkScriptPath;

        // 执行命令行调用 AutoHotkey 脚本
        Process process = Runtime.getRuntime().exec(command);
        process.waitFor();  // 等待脚本执行完成

        // 关闭浏览器
        driver.quit();
    }
    public static void gundon(String[] args) throws IOException, InterruptedException {
        // 设置 ChromeDriver 路径
        System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");

        // 启动浏览器
        WebDriver driver = new ChromeDriver();

        // 打开网页
        driver.get("https://www.example.com");

        // 滚动页面到底部，以确保页面内容加载完毕
        JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
        jsExecutor.executeScript("window.scrollTo(0, document.body.scrollHeight);");
        Thread.sleep(2000);  // 等待页面加载完成

        // 使用 FastStone Capture 进行滚动截屏
        String fastStonePath = "C:\\Program Files (x86)\\FastStone\\FSCapture.exe"; // FastStone Capture 的路径
        String outputPath = "C:\\path\\to\\output\\screenshot.png";  // 保存截图的路径

        // 创建命令行参数：启动滚动截屏
        String command = String.format("\"%s\" /scroll /capture /rect /output=\"%s\"", fastStonePath, outputPath);

        // 执行命令行命令来调用 FastStone Capture 截图
        Process process = Runtime.getRuntime().exec(command);
        process.waitFor();  // 等待命令执行完成

        // 输出结果
        System.out.println("Screenshot saved to: " + outputPath);

        // 关闭浏览器
        driver.quit();
    }
    public static void gundong(String[] args) throws IOException, InterruptedException {
        // 设置 ChromeDriver 路径
        System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");

        // 启动浏览器
        WebDriver driver = new ChromeDriver();

        // 打开页面
        driver.get("https://www.example.com");

        // 获取网页的总高度
        JavascriptExecutor js = (JavascriptExecutor) driver;
        long totalHeight = (long) js.executeScript("return document.body.scrollHeight");

        // 当前视口高度（可以获取屏幕当前可见部分的高度）
        long viewportHeight = (long) js.executeScript("return window.innerHeight");

        // 初始化截图的偏移量
        long scrollPosition = 0;

        // 循环滚动并截屏
        int screenshotCount = 0;
        while (scrollPosition < totalHeight) {
            // 执行截图
            TakesScreenshot screenshot = (TakesScreenshot) driver;
            File srcFile = screenshot.getScreenshotAs(OutputType.FILE);

            // 保存截取的截图
            File destFile = new File("screenshot_" + screenshotCount + ".png");
            //FileUtils.copyFile(srcFile, destFile);

            // 输出保存截图的路径
            System.out.println("Screenshot saved to: " + destFile.getAbsolutePath());

            // 更新滚动位置
            scrollPosition += viewportHeight;
            js.executeScript("window.scrollTo(0, " + scrollPosition + ");");

            // 等待页面加载完成
            Thread.sleep(1000);

            screenshotCount++;
        }

        // 关闭浏览器
        driver.quit();
    }

    public void loadCookies(WebDriver driver) {
        // 从文件加载 cookies
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cookies.ser"))) {
            Object obj = ois.readObject();

            // 确保反序列化的对象是 Set<Cookie> 类型
            if (obj instanceof Set<?> cookieSet) {
                for (Object cookieObj : cookieSet) {
                    if (cookieObj instanceof Cookie) {
                        driver.manage().addCookie((Cookie) cookieObj);
                    } else {
                        System.out.println("Invalid object found in cookie set: " + cookieObj);
                    }
                }
            } else {
                System.out.println("The object is not a valid Set<Cookie>.");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void test(String[] args) {
        // 设置 ChromeDriver 路径
        System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");

        // 设置 ChromeOptions
        ChromeOptions options = new ChromeOptions();

        // 模拟安卓设备，设置设备为 Nexus 5X
        Map<String, Object> mobileEmulation = new HashMap<>();
        mobileEmulation.put("deviceName", "Nexus 5X");
        mobileEmulation.put("width", 1440);  // Nexus 9X 屏幕宽度（假设）
        mobileEmulation.put("height", 2560); // Nexus 9X 屏幕高度（假设）
        mobileEmulation.put("pixelRatio", 3.0);  // Nexus 9X 像素比（假设）
        options.setExperimentalOption("mobileEmulation", mobileEmulation);

        // 设置其它 Chrome 配置选项
        options.addArguments("--disable-blink-features=AutomationControlled"); // 禁用自动化特性,在某些网站上，Selenium 可能被检测为自动化工具，可以通过禁用 webdriver 特性来绕过检测。
        options.addArguments("--disable-extensions");  // 禁用扩展
        options.addArguments("--start-maximized");  // 启动时最大化
        // 设置设备屏幕尺寸和视口，模拟手机端
        options.addArguments("--window-size=375,667");  // iPhone 6分辨率
        //options.addArguments("--window-size=1920,1080");

        options.addArguments("--headless");  // 使用无头模式
        options.addArguments("--disable-gpu");  // 禁用 GPU

        options.addArguments("Accept-Language", "en-US,en;q=0.9");
        options.addArguments("Accept-Encoding", "gzip, deflate, br");
        options.addArguments("Connection", "keep-alive");
        options.addArguments("Upgrade-Insecure-Requests", "1");
        options.setExperimentalOption("useAutomationExtension", false); // 通过 setExperimentalOption 启用一些实验性的 Chrome 功能
        options.addArguments("--force-dark-mode"); //启用浏览器的暗黑模式（Dark Mode）。

        // 禁用代理配置，适用于不需要代理的情况。
        //options.addArguments("--no-proxy-server");
        // 配置 Chrome 浏览器使用代理服务器进行访问。
        options.addArguments("--proxy-server=http://127.0.0.1:8080");

        // 配置自动下载文件的行为，比如设置默认下载目录。
        HashMap<String, Object> prefs = new HashMap<>();
        prefs.put("download.default_directory", "/path/to/download/directory");
        options.setExperimentalOption("prefs", prefs);

        // 禁用 JavaScript，适用于需要快速加载静态页面的情况。
        HashMap<String, Object> prefs1 = new HashMap<>();
        prefs1.put("profile.managed_default_content_settings.javascript", 2);  // 禁用 JS
        options.setExperimentalOption("prefs", prefs1);

        // 禁用图片加载，减少资源消耗和加载时间（通常用于抓取页面时）。
        HashMap<String, Object> prefs2 = new HashMap<>();
        prefs2.put("profile.managed_default_content_settings.images", 2);  // 禁用图片加载
        options.setExperimentalOption("prefs", prefs2);

        // 启用无声的打印：
        // 配置 Chrome 无声打印，避免弹出打印对话框。
        options.addArguments("--disable-popup-blocking");

        // 禁用浏览器弹出的密码保存提示。
        options.addArguments("--disable-save-password");

        // 禁用 Chrome 的网络服务，减少不必要的网络请求。
        options.addArguments("--disable-features=IsolateOrigins,site-per-process");

        // 防止 Chrome 打印时弹出预览对话框。
        options.addArguments("--kiosk-printing");

        // 设置 User-Agent 和其他请求头，模拟真实的安卓设备浏览器
        options.addArguments("user-agent=Mozilla/5.0 (Linux; Android 8.1.0; Nexus 5X Build/OPM4.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Mobile Safari/537.36");
        //options.addArguments("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");

        // 创建 WebDriver 实例
        WebDriver driver = new ChromeDriver(options);

        // 修改 navigator.webdriver 属性，避免自动化脚本的检测。
        //修改 WebDriver 属性: 某些网站会通过 navigator.webdriver 判断是否是自动化浏览器。可以通过 JavaScript 设置这些属性来避免被识别为 Selenium
        JavascriptExecutor js = (JavascriptExecutor) driver;
        js.executeScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})");

        // 在某些环境下，禁用沙盒可以提高浏览器的稳定性。
        options.addArguments("--no-sandbox");

        // 启动时自动最大化浏览器窗口。
        options.addArguments("--start-maximized");

        // 禁用浏览器扩展，减少干扰，提升稳定性。
        options.addArguments("--disable-extensions");

        // 禁用 GPU 加速，适用于无头模式下，提高稳定性。
        options.addArguments("--disable-gpu");

        try {
            // 访问网页
            driver.get("https://www.example.com");

            // 等待页面加载（这里使用 JavaScript 来确保页面完全加载）
            waitForPageLoad(driver);

            // 模拟用户行为：点击、滚动等
            simulateUserBehavior(driver);

            // 获取页面标题和内容
            System.out.println("Page Title: " + driver.getTitle());
            String pageSource = driver.getPageSource();
            System.out.println("Page Content: " + pageSource.substring(0, 200)); // 打印前200个字符

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            driver.quit();  // 关闭浏览器
        }
    }
    // 等待页面加载完成的自定义方法
    private static void waitForPageLoad(WebDriver driver) {
        JavascriptExecutor js = (JavascriptExecutor) driver;
        // 等待页面加载完成（直到没有任何正在加载的资源）
        js.executeScript("return document.readyState").equals("complete");
    }

    // 模拟用户行为：随机滚动和点击
    private static void simulateUserBehavior(WebDriver driver) throws InterruptedException {
        // 模拟滚动
        JavascriptExecutor js = (JavascriptExecutor) driver;
        js.executeScript("window.scrollTo(0, document.body.scrollHeight);");  // 滚动到底部
        Thread.sleep(2000);  // 等待滚动完成

        // 模拟点击
        WebElement someElement = driver.findElement(By.id("some-element-id"));
        someElement.click();  // 点击某个元素
    }

    public static void wechat() {
        // 设置 ChromeDriver 路径
        System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");

        // 创建 ChromeOptions 实例，设置模拟微信浏览器
        ChromeOptions options = new ChromeOptions();

        // 模拟微信浏览器的 User-Agent
        options.addArguments("user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/537.36 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/537.36 MicroMessenger/8.0.2(0x18000235) NetType/WIFI Language/zh_CN");

        // 设置设备屏幕尺寸和视口，模拟手机端
        options.addArguments("--window-size=375,667");  // iPhone 6分辨率
        options.addArguments("--disable-extensions");
        //options.addArguments("--headless");  // 运行在无头模式，不显示浏览器界面（可以去掉来显示浏览器）


        // 启动 Chrome 浏览器并设置为模拟微信浏览器
        WebDriver driver = new ChromeDriver(options);

        try {

            //修改 WebDriver 属性: 某些网站会通过 navigator.webdriver 判断是否是自动化浏览器。可以通过 JavaScript 设置这些属性来避免被识别为 Selenium
            JavascriptExecutor js = (JavascriptExecutor) driver;
            js.executeScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})");
            // 禁用 WebDriver 显示: 一些网站会通过 navigator 和 window 对象检查浏览器是否为自动化浏览器。通过设置一些参数可以规避这些检查。
            // 禁用 'webdriver' 标识符
            options.addArguments("--disable-blink-features=AutomationControlled");
            // 很多网站会检测请求头，确保它们符合浏览器的正常行为。你可以通过 ChromeOptions 设置常见的请求头，比如 Accept, Accept-Language, Connection, Upgrade-Insecure-Requests 等。
            options.addArguments("Accept-Language=en-US,en;q=0.9");
            options.addArguments("Connection=keep-alive");
            options.addArguments("Upgrade-Insecure-Requests=1");
            // 避免过快的请求频率，模拟正常用户的浏览行为。设置合理的等待时间和点击间隔。
            // 随机等待 3 到 7 秒，模拟正常用户行为
            //Thread.sleep(3000 + (long) (Math.random() * 4000));

            driver.get("https://mp.weixin.qq.com");  // 示例：访问微信公众号平台

            // 等待页面加载并执行操作
            Thread.sleep(5000);

            // 打印当前页面标题
            System.out.println("Page Title: " + driver.getTitle());

            // 获取页面源代码
            String pageSource = driver.getPageSource();
            System.out.println("Page Content: " + pageSource.substring(0, 500)); // 打印前500个字符

            // 模拟随机等待
            Thread.sleep(1000 + (long) (Math.random() * 2000));

            // 模拟鼠标移动和点击
            Actions actions = new Actions(driver);
            WebElement element = driver.findElement(By.id("button_id"));
            actions.moveToElement(element).click().perform();

            // 模拟页面滚动
            JavascriptExecutor js1 = (JavascriptExecutor) driver;
            js1.executeScript("window.scrollTo(0, document.body.scrollHeight);");

            // 等待页面加载和自动跳转完成（在 JavaScript 中，跳转可能会延迟 1 秒）
            Thread.sleep(2000); // 假设页面在 1 秒钟后跳转，等待 2 秒确保跳转完成

            // 获取最终的跳转后页面 URL
            String finalUrl = driver.getCurrentUrl();
            System.out.println("Final URL after redirect: " + finalUrl);

            // 你可以在此处获取跳转后的页面内容
            String pageSource1 = driver.getPageSource();
            System.out.println("Page content: " + pageSource1.substring(0, 200)); // 输出前 200 个字符

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            driver.quit();  // 关闭浏览器
        }
    }

    public static void ios() {
        // 设置 ChromeDriver 路径
        System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");

        // 创建 ChromeOptions 实例，设置模拟手机端
        ChromeOptions options = new ChromeOptions();

        // 模拟 iPhone X 或其他设备
        options.addArguments("user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/537.36 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/537.36");

        // 设置设备屏幕尺寸和视口
        options.addArguments("--window-size=375,812"); // iPhone X 分辨率
        options.addArguments("--disable-extensions");
        options.addArguments("--headless");  // 运行在无头模式，不显示浏览器界面（可以去掉来显示浏览器）

        // 启动 Chrome 浏览器并设置为模拟手机
        WebDriver driver = new ChromeDriver(options);

        try {
            driver.get("https://m.example.com");  // 示例：访问移动端网站

            // 等待页面加载并执行操作
            Thread.sleep(5000);

            // 打印当前页面标题
            System.out.println("Page Title: " + driver.getTitle());

            // 执行其他操作，比如获取页面源代码
            String pageSource = driver.getPageSource();
            System.out.println("Page Content: " + pageSource.substring(0, 500)); // 打印前500个字符

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            driver.quit();  // 关闭浏览器
        }
    }

    // 图片识别
    /**
     * Selenium方法等待元素出现
     * @param driver 驱动
     * @param by     元素定位方式
     * @return 元素控件
     */
    public static WebElement WaitMostSeconds(WebDriver driver, By by) {
        try {
            WebDriverWait AppiumDriverWait = new WebDriverWait(driver, Duration.ofSeconds(10));
            return (WebElement) AppiumDriverWait.until(ExpectedConditions
                    .presenceOfElementLocated(by));
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new NoSuchElementException("元素控件未出现");
    }

    /**
     * 保存截图的方法
     * @param screen 元素截图
     * @param name   截图保存名字
     */
    public static void savePng(File screen, String name) {
        String screenShortName = name + ".png";
        try {
            System.out.println("save screenshot");
            FileUtils.copyFile(screen, new File(screenShortName));
        } catch (IOException e) {
            System.out.println("save screenshot fail");
            e.printStackTrace();
        } finally {
            System.out.println("save screenshot finish");
        }
    }

    /**
     * 获取无缺口的验证码和带有缺口的验证码
     */
    public static void saveCode(WebDriver driver) {
        // 获取无缺口的截图
        //((JavascriptExecutor) driver).executeScript("document.querySelectorAll('canvas')[2].style=''");
        WebElement element = WaitMostSeconds(driver, By.cssSelector("div.geetest_window"));
        File screen = element.getScreenshotAs(OutputType.FILE); //执行屏幕截取
        savePng(screen, "无缺口");

        // 获取有缺口的截图
        //((JavascriptExecutor) driver).executeScript("document.querySelectorAll('canvas')[2].classList=[]");
        element = WaitMostSeconds(driver, By.cssSelector("div.geetest_window"));
        screen = element.getScreenshotAs(OutputType.FILE); //执行屏幕截取
        savePng(screen, "有缺口");
    }
    /**
     * 比较两张截图上的当前像素点的RGB值是否相同
     * 只要满足一定误差阈值，便可认为这两个像素点是相同的
     *
     * @param x 像素点的x坐标
     * @param y 像素点的y坐标
     * @return true/false
     */
    public static boolean equalPixel(int x, int y, BufferedImage imgBefore, BufferedImage imgAfter) {
        int rgbaBefore = imgBefore.getRGB(x, y);
        int rgbaAfter = imgAfter.getRGB(x, y);
        // 转化成RGB集合
        Color colBefore = new Color(rgbaBefore, true);
        Color colAfter = new Color(rgbaAfter, true);
        int threshold = 80;   // RGB差值阈值
        return Math.abs(colBefore.getRed() - colAfter.getRed()) < threshold &&
                Math.abs(colBefore.getGreen() - colAfter.getGreen()) < threshold &&
                Math.abs(colBefore.getBlue() - colAfter.getBlue()) < threshold;
    }

    /**
     * 比较两张截图，找出有缺口的验证码截图中缺口所在位置
     * 由于滑块是x轴方向位移，因此只需要x轴的坐标即可
     *
     * @return 缺口起始点x坐标
     * @throws Exception
     */
    public static int getGap() throws Exception {
        BufferedImage imgBefore = ImageIO.read(new File("有缺口.png"));
        BufferedImage imgAfter = ImageIO.read(new File("无缺口.png"));
        int width = imgBefore.getWidth();
        int height = imgBefore.getHeight();
        int pos = 60;  // 小方块的固定起始位置
        // 横向扫描
        for (int i = pos; i < width; i++) {
            for (int j = 0; j < height; j++) {
                if (!equalPixel(i, j, imgBefore, imgAfter)) {
                    pos = i;
                    return pos;
                }
            }
        }
        throw new Exception("未找到滑块缺口");
    }
    /**
     * 计算滑块到达目标点的运行轨迹
     * 先加速，后减速
     * @param distance 目标距离
     * @return 运动轨迹
     */
    public static List<Integer> trace(int distance) {
        List<Integer> moveTrace = new ArrayList<>();
        int current = 0;  // 当前位移
        int threshold = distance * 3 / 5; // 减速阈值
        double t = 0.2;   // 计算间隔
        double v = 0.0;     // 初速度
        double a;     // 加速度
        while (current < distance) {
            if (current < threshold) {
                a = 2;
            } else {
                a = -4;
            }
            // 位移计算公式
            double tmp = v;
            // 移动速度，会出现负值的情况，然后往反方向拉取
            v = tmp + a * t;
            int move = (int) (tmp * t + 0.5 * a * t * t);
            current += move;
            moveTrace.add(move);
        }
        // 考虑到最后一次会超出移动距离，将其强制修改回来，不允许超出
        int length = moveTrace.size();
        moveTrace.set(length - 1, moveTrace.get(length - 1) + (current > distance ? -(current - distance) : 0));
        return moveTrace;
    }
    /**
     * 消除selenium中移动操作的卡顿感
     * 这种卡顿感是因为selenium中自带的moveByOffset是默认有200ms的延时的
     * 可参考:https://blog.csdn.net/fx9590/article/details/113096513
     *
     * @param x x轴方向位移距离
     * @param y y轴方向位移距离
     */
    public static void moveWithoutWait(int x, int y, WebDriver driver) {
        PointerInput defaultMouse = new PointerInput(PointerInput.Kind.MOUSE, "default mouse");
        (new Actions(driver)).tick(defaultMouse.createPointerMove(Duration.ofMillis(0), PointerInput.Origin.pointer(), x, y)).perform();
    }
    /**
     * 移动滑块，实现验证
     * @param moveTrace 滑块的运动轨迹
     * @throws Exception
     */
    public static void move(List<Integer> moveTrace, WebDriver driver) throws Exception {
        Actions actions = new Actions(driver);
        // 获取滑块对象
        WebElement element = WaitMostSeconds(driver, By.cssSelector("div.geetest_slider_button"));
        // 按下滑块
        actions.clickAndHold(element).perform();
        for (Integer integer : moveTrace) {
            // 位移一次
            int dis = (int) integer;
            moveWithoutWait(dis, 0,driver);
        }
        // 模拟人的操作，超过区域
        moveWithoutWait(5, 0,driver);
        moveWithoutWait(-3, 0,driver);
        moveWithoutWait(-2, 0,driver);
        // 释放滑块
        actions.release().perform();
        Thread.sleep(500);
    }


}
